/**
 * @file container.h
 * @author <raphael.londeix@gmail.com> Raphaël Londeix
 *
 * @section DESCRIPTION
 * @FIXME@
 */

#ifndef __CONTAINER_H__
# define __CONTAINER_H__

# include "class.h"
# include "raise.h"

// Container is an interface


typedef struct ContainerMetaClass ContainerMetaClass;
struct ContainerMetaClass
{
    MetaClass const         __metaclass__; // allow container pointer to act as a MetaClass

    // Container methods
    void        (*__push_front__)(Object* this, va_list* ap);
    void        (*__push_back__)(Object* this, va_list* ap);
    void        (*__pop_front__)(Object* this);
    void        (*__pop_back__)(Object* this);
    void        (*__append__)(Object* this, va_list* ap);
    void        (*__erase__)(Object* this, va_list* ap);
    void        (*__clear__)(Object* this);
    Object*     (*__front__)(Object* this);
    Object*     (*__back__)(Object* this);
    size_t      (*__size__)(Object* this);
    void        (*__setitem__)(Object* this, va_list* ap);
    Object*     (*__getitem__)(Object* this, va_list* ap);
    Object*     (*__begin__)(Object* this);
    Object*     (*__end__)(Object* this);
};

extern Class const* const Container;

#define call_container_method(this, method, ...) ({ \
    MetaClass const* tmp = typeof(this); \
    while (tmp != NULL) \
    { \
        MetaClass const* par = get_parent_type(tmp); \
        if (par == Container->__metaclass__) \
            break; \
        tmp = par; \
    } \
    if (tmp == NULL) \
        raise("Class '", typeof(this)->__name__, "' is not a container"); \
    else if (((ContainerMetaClass const*) tmp)->__##method##__ == NULL) \
        raise("Class '", typeof(this)->__name__, "' has no method '" #method "'"); \
    ((ContainerMetaClass const*) tmp)->__##method##__((Object*) up_cast(this, tmp), ## __VA_ARGS__); \
})

void push_back(Object* this, ...);
void push_front(Object* this, ...);

#define size(this) call_container_method(this, size)

#define front(this) call_container_method(this, front)
#define front_char(obj) (*((char* const) front(obj)))
#define front_int(obj) (*((int* const) front(obj)))
#define front_int32(obj) (*((int32_t* const) front(obj)))
#define front_uint32(obj) (*((uint32_t* const) front(obj)))
#define front_int64(obj) (*((int64_t* const) front(obj)))
#define front_uint64(obj) (*((uint64_t* const) front(obj)))
#define front_float(obj) (*((float* const) front(obj)))
#define front_double(obj) (*((double* const) front(obj)))
#define front_ptr(obj) (*((void* const*) front(obj)))

#define back(this) call_container_method(this, back)
#define back_char(obj) (*((char* const) back(obj)))
#define back_int(obj) (*((int* const) back(obj)))
#define back_int32(obj) (*((int32_t* const) back(obj)))
#define back_uint32(obj) (*((uint32_t* const) back(obj)))
#define back_int64(obj) (*((int64_t* const) back(obj)))
#define back_uint64(obj) (*((uint64_t* const) back(obj)))
#define back_float(obj) (*((float* const) back(obj)))
#define back_double(obj) (*((double* const) back(obj)))

#define pop_front(this) call_container_method(this, pop_front)
#define pop_back(this) call_container_method(this, pop_back)


#define clear(this) call_container_method((this), clear)

void setitem(Object* this, ...);
Object* getitem(Object* this, ...);
#define getitem_char(this, ...) (*((char* const) getitem(this, __VA_ARGS__)))
#define getitem_ptr(this, ...) (*((void* const*) getitem(this, __VA_ARGS__)))

# define CONTAINER_METACLASS_NAME "Container"

# define IMPL_CONTAINER_METACLASS(\
        cls, base,\
        ctor, dtor, str, add, sub, mul, div, eq, gt, lt, \
        method_push_front, \
        method_push_back, \
        method_pop_front, \
        method_pop_back, \
        method_append, \
        method_erase, \
        method_clear, \
        method_front, \
        method_back, \
        method_size, \
        method_setitem, \
        method_getitem, \
        method_begin, \
        method_end \
    ) \
{ \
    IMPL_METACLASS( \
        base, \
        &cls, \
        sizeof(cls##Class), \
        #cls, \
        ctor, dtor, str, \
        add, sub, mul, div, eq, gt, lt\
    ), \
    (void (*)(Object*, va_list*))       method_push_front, \
    (void (*)(Object*, va_list*))       method_push_back, \
    (void (*)(Object*))                 method_pop_front, \
    (void (*)(Object*))                 method_pop_back, \
    (void (*)(Object*, va_list*))       method_append, \
    (void (*)(Object*, va_list*))       method_erase, \
    (void (*)(Object*))                 method_clear, \
    (Object* (*)(Object* ))             method_front, \
    (Object* (*)(Object* ))             method_back, \
    (size_t (*)(Object*))               method_size, \
    (void (*)(Object*, va_list*))       method_setitem, \
    (Object* (*)(Object*, va_list*))    method_getitem, \
    (Object* (*)(Object* ))             method_begin, \
    (Object* (*)(Object* ))             method_end, \
}\

#define IMPL_CONTAINER_CLASS( \
    cls, ctor, dtor, str, add, sub, mul, div, eq, gt, lt, \
    method_push_front, \
    method_push_back, \
    method_pop_front, \
    method_pop_back, \
    method_append, \
    method_erase, \
    method_clear, \
    method_front, \
    method_back, \
    method_size, \
    method_setitem, \
    method_getitem, \
    method_begin, \
    method_end, \
    ...\
) \
static ContainerMetaClass const __##cls##MetaClass = IMPL_CONTAINER_METACLASS( \
    cls, &Container, ctor, dtor, str, add, sub, mul, div, eq, gt, lt, \
    method_push_front, \
    method_push_back, \
    method_pop_front, \
    method_pop_back, \
    method_append, \
    method_erase, \
    method_clear, \
    method_front, \
    method_back, \
    method_size, \
    method_setitem, \
    method_getitem, \
    method_begin, \
    method_end \
); \
cls##Class __##cls##Description = {{(MetaClass const*) &__##cls##MetaClass}, __VA_ARGS__}; \
Class const* const cls = (Class const* const) &__##cls##Description

#endif /* !__CONTAINER_H__ */

